// Copyright (c) 2024 CHANGLEI. All rights reserved.

import 'package:charts_grasp/src/chart/axis/axis.dart';
import 'package:charts_grasp/src/chart/base/chart_base.dart';
import 'package:charts_grasp/src/chart/chart_segment/chart_segment.dart';
import 'package:charts_grasp/src/chart/chart_segment/error_bar_segment.dart';
import 'package:charts_grasp/src/chart/chart_series/error_bar_series.dart';
import 'package:charts_grasp/src/chart/chart_series/series.dart';
import 'package:charts_grasp/src/chart/chart_series/series_renderer_properties.dart';
import 'package:charts_grasp/src/chart/chart_series/xy_data_series.dart';
import 'package:charts_grasp/src/chart/common/cartesian_state_properties.dart';
import 'package:charts_grasp/src/chart/common/common.dart';
import 'package:charts_grasp/src/chart/common/renderer.dart';
import 'package:charts_grasp/src/chart/common/segment_properties.dart';
import 'package:charts_grasp/src/chart/utils/helper.dart';
import 'package:charts_grasp/src/common/event_args.dart';
import 'package:flutter/material.dart';

class ErrorBarSeriesRenderer extends XyDataSeriesRenderer {
  ErrorBarSeriesRenderer();

  late SeriesRendererDetails _currentSeriesDetails;
  late SeriesRendererDetails _segmentSeriesDetails;

  ChartSegment _createSegments(
      CartesianChartPoint<dynamic> currentPoint, int pointIndex, int seriesIndex, double animateFactor,) {
    _currentSeriesDetails = SeriesHelper.getSeriesRendererDetails(this);
    final ErrorBarSegment segment = createSegment() as ErrorBarSegment;
    SegmentHelper.setSegmentProperties(segment, SegmentProperties(_currentSeriesDetails.stateProperties, segment));
    final SegmentProperties segmentProperties = SegmentHelper.getSegmentProperties(segment);
    final ErrorBarSeries<dynamic, dynamic> errorBarSeries =
        _currentSeriesDetails.series as ErrorBarSeries<dynamic, dynamic>;
    segmentProperties.seriesRenderer = this;
    segmentProperties.series = errorBarSeries;
    segmentProperties.seriesIndex = seriesIndex;
    segment.currentSegmentIndex = pointIndex;
    segment.animationFactor = animateFactor;
    if (errorBarSeries.onRenderDetailsUpdate != null &&
        _currentSeriesDetails.animationController.status == AnimationStatus.completed) {
      final ErrorBarRenderDetails errorBarRenderDetails = ErrorBarRenderDetails(
          currentPoint.visiblePointIndex, currentPoint.overallDataPointIndex, currentPoint.errorBarValues,);
      errorBarSeries.onRenderDetailsUpdate!(errorBarRenderDetails);
    }
    segmentProperties.currentPoint = currentPoint;
    _segmentSeriesDetails = SeriesHelper.getSeriesRendererDetails(segmentProperties.seriesRenderer);
    customizeSegment(segment);
    _currentSeriesDetails.segments.add(segment);
    return segment;
  }


  @override
  ChartSegment createSegment() => ErrorBarSegment();

  @override
  void customizeSegment(ChartSegment segment) {
    final ErrorBarSegment errorBarSegment = segment as ErrorBarSegment;
    final SegmentProperties segmentProperties = SegmentHelper.getSegmentProperties(errorBarSegment);
    segmentProperties.color = segmentProperties.currentPoint!.pointColorMapper ?? _segmentSeriesDetails.seriesColor;
    errorBarSegment.strokePaint = errorBarSegment.getStrokePaint();
  }

  @override
  void drawDataMarker(int index, Canvas canvas, Paint fillPaint, Paint strokePaint, double pointX, double pointY,
      [CartesianSeriesRenderer? seriesRenderer,]) {}

  @override
  void drawDataLabel(
      int index, Canvas canvas, String dataLabel, double pointX, double pointY, int angle, TextStyle style,) {}
}

class ErrorBarChartPainter extends CustomPainter {
  ErrorBarChartPainter(
      {required this.stateProperties,
      required this.seriesRenderer,
      required this.isRepaint,
      required this.animationController,
      required ValueNotifier<num> notifier,
      required this.painterKey,})
      : chart = stateProperties.chart,
        super(repaint: notifier);

  final CartesianStateProperties stateProperties;

  final CgCartesianChart chart;

  final bool isRepaint;

  final AnimationController animationController;

  List<ChartLocation> currentChartLocations = <ChartLocation>[];

  ErrorBarSeriesRenderer seriesRenderer;

  final PainterKey painterKey;

  @override
  void paint(Canvas canvas, Size size) {
    final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(seriesRenderer);
    disposeOldSegments(chart, seriesRendererDetails);
    final ChartAxisRendererDetails xAxisDetails = seriesRendererDetails.xAxisDetails!;
    final ChartAxisRendererDetails yAxisDetails = seriesRendererDetails.yAxisDetails!;
    final List<CartesianChartPoint<dynamic>> dataPoints = seriesRendererDetails.dataPoints;
    final ErrorBarSeries<dynamic, dynamic> series = seriesRendererDetails.series as ErrorBarSeries<dynamic, dynamic>;
    if (seriesRendererDetails.visible! == true) {
      Rect axisClipRect;
      double animationFactor;
      CartesianChartPoint<dynamic> point;
      canvas.save();
      assert(series.animationDuration >= 0,
          'The animation duration of the error bar series must be greater than or equal to 0.',);
      final int seriesIndex = painterKey.index;
      seriesRendererDetails.storeSeriesProperties(stateProperties, seriesIndex);
      axisClipRect = calculatePlotOffset(
          stateProperties.chartAxis.axisClipRect, Offset(xAxisDetails.axis.plotOffset, yAxisDetails.axis.plotOffset),);
      canvas.clipRect(axisClipRect);
      animationFactor =
          seriesRendererDetails.seriesAnimation != null ? seriesRendererDetails.seriesAnimation!.value : 1;
      stateProperties.shader = null;
      if (series.onCreateShader != null) {
        stateProperties.shader =
            series.onCreateShader!(ShaderDetails(stateProperties.chartAxis.axisClipRect, 'series'));
      }
      int segmentIndex = -1;
      if (seriesRendererDetails.visibleDataPoints == null ||
          seriesRendererDetails.visibleDataPoints!.isNotEmpty == true) {
        seriesRendererDetails.visibleDataPoints = <CartesianChartPoint<dynamic>>[];
      }
      seriesRendererDetails.setSeriesProperties(seriesRendererDetails);
      for (int pointIndex = 0; pointIndex < dataPoints.length; pointIndex++) {
        point = dataPoints[pointIndex];
        final bool withInXRange = withInRange(point.xValue, seriesRendererDetails.xAxisDetails!);
        final bool withInYRange =
            point.yValue != null && withInRange(point.yValue, seriesRendererDetails.yAxisDetails!);
        if (withInXRange || withInYRange) {
          seriesRendererDetails.calculateRegionData(
              stateProperties, seriesRendererDetails, painterKey.index, point, pointIndex,);
          if (point.isVisible && !point.isGap) {
            seriesRendererDetails.drawSegment(
                canvas, seriesRenderer._createSegments(point, segmentIndex += 1, painterKey.index, animationFactor),);
          }
        }
      }
      canvas.restore();
      if (animationFactor >= 1) {
        stateProperties.setPainterKey(painterKey.index, painterKey.name, true);
      }
    }
  }

  @override
  bool shouldRepaint(ErrorBarChartPainter oldDelegate) => isRepaint;
}
